package com.example.demo.service.impl;

import cn.hutool.extra.mail.MailAccount;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.dto.ApiTestCaseGroupResultDto;
import com.example.demo.entity.dto.ApiTestCaseResultDto;
import com.example.demo.enums.PlanStatusEnum;
import com.example.demo.mapper.*;
import com.example.demo.entity.*;
import com.example.demo.entity.response.job.JobDetailResp;
import com.example.demo.service.ApiTestCaseStepService;
import com.example.demo.service.JobMsgService;
import com.example.demo.common.contant.ScheduleConstants;
import com.example.demo.utils.*;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class JobMsgServiceImpl implements JobMsgService {

    @Resource
    Scheduler scheduler;
    @Resource
    JobMsgMapper jobMsgMapper;
    @Resource
    JobPlanLogMapper jobPlanLogMapper;

    @Resource
    ProjectMapper projectMapper;

    @Resource
    ApiTestCaseGroupMapper apiTestCaseGroupMapper;

    @Resource
    ApiRequestResultMapper apiRequestResultMapper;

    @Resource
    ApiTestCaseResultMapper apiTestCaseResultMapper;

    @Resource
    ApiTestCaseMergeMapper apiTestCaseMergeMapper;

    @Resource
    ApiTestCaseStepService apiTestCaseStepService;

    @Resource
    ApiTestCaseGroupResultMapper apiTestCaseGroupResultMapper;

    @Resource
    private SpringTemplateEngine templateEngine;


    @Value("${emailProperties.emailUser}")
    private String emailUser;

    @Value("${emailProperties.emailPassword}")
    private String emailPassword;

    @Value("${emailProperties.emailHost}")
    private String emailHost;

    @Value("${emailProperties.emailPort}")
    private int emailPort;

    @Value("${server.port}")
    private int webPort;

    ApiTestCaseGroupResult apiTestCaseGroupResult = new ApiTestCaseGroupResult();

    @Override
    public int add(JobMsg jobMsg) {
        jobMsg.setCreateTime(DateToStamp.getTimeStamp());
        jobMsg.setUpdateTime(DateToStamp.getTimeStamp());
        int rows = jobMsgMapper.add(jobMsg);
        if (rows > 0) {
            SchedulerUtil.createScheduler(scheduler, jobMsg);
        }
        return rows;
    }

    @Override
    public PageInfoNew<JobMsg> jobMsgPage(int pageNum, int pageSize, int projectId) {
        PageHelper.startPage(pageNum, pageSize);
        return new PageInfoNew<>(jobMsgMapper.selectAllByCondition(projectId));
    }

    @Override
    public JobDetailResp jobDetail(int id) {
        JobMsg jobMsg = jobMsgMapper.selectById(id);
        JobDetailResp jobPageResp = new JobDetailResp();
        BeanUtils.copyProperties(jobMsg, jobPageResp);
        List<Integer> groupIdList = jobMsg.getGroupIdList();
        List<ApiTestCaseGroup> apiTestCaseGroupList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(groupIdList)) {
            for (Integer groupId : groupIdList) {
                ApiTestCaseGroup apiTestCaseGroup = apiTestCaseGroupMapper.selectById(groupId);
                apiTestCaseGroupList.add(apiTestCaseGroup);
            }
        }
        jobPageResp.setGroupList(apiTestCaseGroupList);
        return jobPageResp;
    }

    @Override
    public int updateJob(JobMsg jobMsg) {
        jobMsg.setUpdateTime(DateToStamp.getTimeStamp());
        int row = jobMsgMapper.updateJob(jobMsg);
        if (row > 0) {
            SchedulerUtil.updateJob(scheduler, jobMsg);
        }
        return row;
    }

    @Override
    public int deleteJob(int id) {
        int row = jobMsgMapper.deleteJobById(id);
        if (row > 0) {
            SchedulerUtil.deleteJob(scheduler, id);
            jobPlanLogMapper.delete(id);
        }
        return 0;
    }

    @Override
    public void modifyState(JobMsg jobMsg) {
        if (jobMsg.getStatus() == ScheduleConstants.Status.NORMAL.getValue()) {
            SchedulerUtil.resumeJob(scheduler, jobMsg.getId());
        } else if (jobMsg.getStatus() == ScheduleConstants.Status.PAUSE.getValue()) {
            SchedulerUtil.pauseJob(scheduler, jobMsg.getId());
        }
        jobMsgMapper.modifyState(jobMsg);
    }

    @Override
    public void excJob(JobMsg jobMsg) {
        long startTime = System.currentTimeMillis();
        int groupCountTotal = 0, groupPassTotal = 0, groupFailedTotal = 0, caseSkipTotal = 0, casePassTotal = 0, caseFailedTotal = 0, testCaseTotal = 0;
        JobPlanLog jobPlanLog = new JobPlanLog();
        List<Integer> groupIdList = jobMsg.getGroupIdList();
        groupCountTotal = groupIdList.size();
        jobPlanLog.setJobId(jobMsg.getId());
        jobPlanLog.setProjectId(jobMsg.getProjectId());
        jobPlanLog.setGroupTotal(groupCountTotal);
        jobPlanLog.setFailedTotal(0);
        jobPlanLog.setPassTotal(0);
        jobPlanLog.setStatus(PlanStatusEnum.ONGOING_STATUS.getValue());
        jobPlanLog.setCreateTime(DateToStamp.getTimeStamp());
        jobPlanLogMapper.insert(jobPlanLog);
        apiTestCaseGroupResult.setPlanId(jobPlanLog.getId());
        try {
            for (int groupId : groupIdList) {
                ApiTestCaseGroupResultDto apiTestCaseGroupResultDto = runTestGroup(groupId, jobMsg.getEnvId());
                caseFailedTotal += apiTestCaseGroupResultDto.getApiTestCaseGroupResult().getFailedResult();
                casePassTotal += apiTestCaseGroupResultDto.getApiTestCaseGroupResult().getPassResult();
                caseSkipTotal += apiTestCaseGroupResultDto.getApiTestCaseGroupResult().getSkipResult();
                testCaseTotal += apiTestCaseGroupResultDto.getApiTestCaseGroupResult().getCountResult();
                ApiTestCaseResult apiTestCaseResult = apiTestCaseGroupResultDto.getApiTestCaseResult();
                apiTestCaseResult.setPlanId(jobPlanLog.getId());
                apiTestCaseResult.setGroupId(groupId);
                apiTestCaseResultMapper.insertResult(apiTestCaseResult);
                if (apiTestCaseGroupResultDto.getApiTestCaseGroupResult().getFailedResult() > 0) {
                    groupFailedTotal += 1;
                } else {
                    groupPassTotal += 1;
                }
                for (ApiRequestResult apiRequestResult : apiTestCaseGroupResultDto.getApiRequestResults()) {
                    apiRequestResult.setPlanId(jobPlanLog.getId());
                    apiRequestResultMapper.insertApiRequestResult(apiRequestResult);
                }
            }
            jobPlanLog.setFailedTotal(groupFailedTotal);
            jobPlanLog.setPassTotal(groupPassTotal);
            jobPlanLog.setTestCaseTotal(testCaseTotal);
            jobPlanLog.setTestCaseFailedTotal(caseFailedTotal);
            jobPlanLog.setTestCasePassTotal(casePassTotal);
            jobPlanLog.setTestCaseSkipTotal(caseSkipTotal);
            if (groupFailedTotal == 0) {
                jobPlanLog.setStatus(PlanStatusEnum.PASS_STATUS.getValue());
            } else {
                jobPlanLog.setStatus(PlanStatusEnum.FAILED_STATUS.getValue());
            }
        } catch (Exception e) {
            log.error("任务执行报错:{}", e.toString());
            jobPlanLog.setStatus(PlanStatusEnum.EXCEPTION_STATUS.getValue());
            jobPlanLog.setExceptionLog(e.toString());
        } finally {
            //不管结果如何，都需要保存结果
            long endTime = System.currentTimeMillis();

            BigDecimal timeConsuming = BigDecimal.valueOf((double) endTime / 1000 - (double) startTime / 1000);
            timeConsuming = timeConsuming.setScale(2, BigDecimal.ROUND_HALF_UP);
            jobPlanLog.setTimeConsuming(timeConsuming);
            log.info("打印需要更新的jobPlanLog:{}", JSONObject.toJSON(jobPlanLog));
            jobPlanLogMapper.update(jobPlanLog);
            //钉钉群推送
            try {
                Project project = projectMapper.detail(jobMsg.getProjectId());
                if (!project.getDingDingAutograph().isEmpty() && !project.getDingDingTalkUrl().isEmpty() && jobMsg.getIsSendDingDing()) {
                    log.info("DingDingAutograph:{},DingDingTalkUrl:{},isSendDingDing:{}", project.getDingDingAutograph(), project.getDingDingTalkUrl(), jobMsg.getIsSendDingDing());
                    SendDingDingTalkUtils.sedDingDingTalk(getContent(jobMsg, jobPlanLog), project.getDingDingAutograph(), project.getDingDingTalkUrl());
                    log.info("钉钉推送成功，推送数据为：{}", getContent(jobMsg, jobPlanLog));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            //邮件推送，判断是否新增邮件信息
            sendEmail(jobMsg, jobPlanLog);
        }

    }

    /**
     * 邮件推送，判断是否新增邮件信息
     *
     * @param jobMsg     任务信息
     * @param jobPlanLog 任务计划数据
     */
    public void sendEmail(JobMsg jobMsg, JobPlanLog jobPlanLog) {
        if (jobMsg.getIsSendEmail() && jobMsg.getEmailList().size() > 0) {
            MailAccount account = new MailAccount();
            account.setAuth(true);
            account.setSslEnable(true);
            account.setStarttlsEnable(true);
            account.setPort(emailPort);
            account.setHost(emailHost);
            account.setFrom(emailUser);
            account.setUser(emailUser);
            account.setPass(emailPassword);
            SendEmailUtils sendEmailUtils = new SendEmailUtils();
            String content = templateSendEmail(jobMsg, jobPlanLog);
            sendEmailUtils.send(account, jobMsg.getEmailList(), jobMsg.getJobName(), content);
            log.info("邮件推送成功，推送数据为：{}", content);
        }
    }

    /**
     * 解析html，获取邮件html内容
     *
     * @param jobMsg     任务信息
     * @param jobPlanLog 任务计划数据
     * @return 返回邮件html内容
     */
    public String templateSendEmail(JobMsg jobMsg, JobPlanLog jobPlanLog) {
        Map<String, Object> params = new HashMap<>();
        Context context = new Context();
        params.put("jobName", "亲爱的Tester：您好！以下是接口自动化任务【".concat(jobMsg.getJobName()).concat("】执行情况。"));
        params.put("createTime", DateToStamp.stampToDate(jobPlanLog.getCreateTime()));
        params.put("groupTotal", String.valueOf(jobPlanLog.getGroupTotal()));
        params.put("timeConsuming", String.valueOf(jobPlanLog.getTimeConsuming()));
        params.put("testCaseTotal", String.valueOf(jobPlanLog.getTestCaseTotal()));
        params.put("testCasePassTotal", String.valueOf(jobPlanLog.getTestCasePassTotal()));
        params.put("testCaseFailedTotal", String.valueOf(jobPlanLog.getTestCaseFailedTotal()));
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            log.info("inetAddress:{}", inetAddress);
            String address = String.valueOf(inetAddress).split("/")[1];
            String jobPlanDetailPath = "http://".concat(address).concat(":").
                    concat(String.valueOf(webPort)).concat("/#/reportDetail?id=").
                    concat(String.valueOf(jobPlanLog.getId()));
            params.put("jobPlanDetailPath", jobPlanDetailPath);
            params.put("webip", address);
            params.put("webport", webPort);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        context.setVariable("params", params);
        context.setVariable("title", "title");
        context.setVariable("heading", "heading");
        context.setVariable("content", "content");
        String content = templateEngine.process("sendEmail", context);
        return content;

    }

    /**
     * 跟进jobPlanLog 拼接邮件内容,可用于钉钉推送
     *
     * @param jobPlanLog
     * @return
     */
    public String getContent(JobMsg jobMsg, JobPlanLog jobPlanLog) {
        InetAddress inetAddress = null;
        try {
            inetAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        log.info("inetAddress:{}", inetAddress);
        String address = String.valueOf(inetAddress).split("/")[1];
        String jobPlanDetailPath = "http://".concat(address).concat(":").
                concat(String.valueOf(webPort)).concat("/#/reportDetail?id=").
                concat(String.valueOf(jobPlanLog.getId()));
        String content;
        log.info("打印jobPlanLog信息：{}", JSONObject.toJSON(jobPlanLog));
        String jobName = "亲爱的Tester：您好！以下是接口自动化任务【".concat(jobMsg.getJobName()).concat("】执行情况。\n");
        String groupTotal = String.format("测试用例集共：%s", jobPlanLog.getGroupTotal()).concat("条").concat("，总耗时：").concat(String.valueOf(jobPlanLog.getTimeConsuming())).concat("秒\n");
        String passTotal = String.format("测试用例集通过：%s", jobPlanLog.getPassTotal()).concat("条\n");
        String failedTotal = String.format("测试用例集失败：%s", jobPlanLog.getFailedTotal()).concat("条\n");
        String testCaseTotal = String.format("测试用例共：%s", jobPlanLog.getTestCaseTotal()).concat("条\n");
        String testCasePassTotal = String.format("用例执行成功共：%s", jobPlanLog.getTestCasePassTotal()).concat("条\n");
        String testCaseFailedTotal = String.format("用例执行失败共：%s", jobPlanLog.getTestCaseFailedTotal()).concat("条\n");
        content = jobName.concat(groupTotal).concat(passTotal).
                concat(failedTotal).concat(testCaseTotal).concat(testCasePassTotal).
                concat(testCaseFailedTotal).concat("详情请查看平台报告\n").concat("报告地址：").concat(jobPlanDetailPath);
        return content;
    }

    @Override
    public int run(int jobId) {
        log.info("获取jobMsg：" + JSONObject.toJSONString(jobMsgMapper.selectById(jobId)));
        JobMsg jobMsg = jobMsgMapper.selectById(jobId);
        return SchedulerUtil.run(scheduler, jobMsg);
    }

    public ApiTestCaseGroupResultDto runTestGroup(int testCaseGroupId, int envId) {
        ApiTestCaseGroupResultDto apiTestCaseGroupResultDto = new ApiTestCaseGroupResultDto();
        int count = 0, pass = 0, failed = 0;
        ApiTestCaseResultDto apiTestCaseResultDto = new ApiTestCaseResultDto();
        List<ApiTestCaseMerge> apiTestCaseMergeList = apiTestCaseMergeMapper.findByGroupId(testCaseGroupId);
        count = apiTestCaseMergeList.size();
        if (count > 0) {
            for (ApiTestCaseMerge apiTestCaseMerge : apiTestCaseMergeList) {
                apiTestCaseResultDto = apiTestCaseStepService.runStep(apiTestCaseMerge.getApiTestCaseId(), envId);
                BeanUtils.copyProperties(apiTestCaseResultDto, apiTestCaseGroupResultDto);
                if (apiTestCaseGroupResultDto.getApiTestCaseResult().getFailedResults() > 0) {
                    failed += 1;
                } else {
                    pass += 1;
                }
            }
        }
        apiTestCaseGroupResult.setTestCaseGroupName(apiTestCaseGroupMapper.selectById(testCaseGroupId).getName());
        apiTestCaseGroupResult.setCountResult(count);
        apiTestCaseGroupResult.setTestCaseGroupId(testCaseGroupId);
        apiTestCaseGroupResult.setFailedResult(failed);
        apiTestCaseGroupResult.setPassResult(pass);
        apiTestCaseGroupResult.setCreateTime(DateToStamp.getTimeStamp());
        apiTestCaseGroupResult.setUpdateTime(DateToStamp.getTimeStamp());
        apiTestCaseGroupResultMapper.insertResult(apiTestCaseGroupResult);
        apiTestCaseGroupResultDto.setApiTestCaseGroupResult(apiTestCaseGroupResult);
        return apiTestCaseGroupResultDto;
    }

}
